home *** CD-ROM | disk | FTP | other *** search
- /* Send and receive IP datagrams on serial lines. Compatible with SL/FP
- * as used with the Merit Network and MIT.
- */
- #include <stdio.h>
- #include "global.h"
- #include "mbuf.h"
- #include "iface.h"
- #include "timer.h"
- #include "ip.h"
- #include "slfp.h"
- #ifdef UNIX /* BSD or SYS5 */
- #include "unix.h"
- #else
- # ifdef ATARI_ST
- # include "st.h"
- # else
- # include "pc.h"
- # include "asy.h"
- # endif /* ATARI_ST */
- #endif /* BSD or SYS5 */
- #include "trace.h"
-
- int asy_ioctl();
- int slfp_send();
- int doslfp();
- int asy_output();
-
- /* SL/FP level control structure */
- struct slfp slfp[ASY_MAX];
- char slfp_ack[ACK_LEN] = { SLFP_ACK } ;
- char slfp_req[REQ_LEN] = { SLFP_REQ } ;
- char ip_hdr[HDR_LEN] = { 2, 1, 0, 0 } ; /* IP Packet Header */
- char ar_hdr[HDR_LEN] = { 2, 3, 0, 0 } ; /* "Addr Req" Packet Header */
- struct interface asy_interface = /* Fake interface for "dump" proc */
- { NULLIF, "asy" } ; /* Name of "asy" interface */
-
- /* Routine to Initialize the Async line for SL/FP processing.
- * Mostly involves requesting the IP Address for this host.
- */
- int
- slfp_init(interface,pip_addr,modem_cmd)
- struct interface *interface ;
- int32 *pip_addr ; /* pointer to host ip address */
- char *modem_cmd ; /* optional command to Modem */
- {
- register struct slfp *sp;
- register struct timer *ar ;
- char *modem_line ;
- int i ;
-
- sp = &slfp[interface->dev];
- ar = &sp->ar_timer ;
- sp->ar_pending = 1 ;
- slfp[interface->dev].req_pending = 0 ;
-
- /* If a Modem Command is present, send it and wait for Connection */
- if (modem_cmd != NULLCHAR) {
- char c; c='\r';
- modem_line = (char *)malloc(strlen(modem_cmd)+2) ;
- if (modem_line == NULLCHAR)
- return -1 ;
- strcpy(modem_line, modem_cmd);
- strcat(modem_line, "\r") ;
- asy_output(interface->dev,&c,1); /* Wake up modem */
- set_timer(ar,500);
- start_timer(ar);
- while(ar->state == TIMER_RUN)
- keep_things_going();
- asy_output(interface->dev,modem_line,strlen(modem_line));
- set_timer(ar, 30000) ; /* Wait upto 30 seconds for Connection */
- } else
- set_timer(ar, 500) ; /* Wait half a second before sending REQ's */
-
- /* Request an IP Address upto 4 times (every 24 seconds) before giving up */
- for (i=0; i<4; i++) {
- start_timer(ar) ;
- while (sp->ar_pending && (ar->state == TIMER_RUN)) {
- keep_things_going() ; /* Can't return until timeout or addr */
- if(kbread()==(-2)) /* Hit the Escape key */
- return(-1);
- }
- if (!sp->ar_pending) {
- if (modem_cmd != NULLCHAR)
- free(modem_line) ;
- return 0 ;
- }
- slfp_send(NULLBUF, interface, 0L, 0, 0, 0, 0) ;
- set_timer(ar, 24000) ; /* Wait upto 24 seconds for IP Addr */
- }
-
- if (modem_cmd != NULLCHAR)
- free(modem_line) ;
- sp->ar_pending = 0 ;
- return -1 ;
- }
-
- /* Send routine for point-to-point slfp
- * This is a trivial function since slfp_encode adds the link-level header
- */
- int
- slfp_send(bp,interface,gateway,precedence,delay,throughput,reliability)
- struct mbuf *bp; /* Buffer to send */
- struct interface *interface; /* Pointer to interface control block */
- int32 gateway;
- char precedence;
- char delay;
- char throughput;
- char reliability;
- {
- if(interface == NULLIF){
- free_p(bp);
- return;
- }
- dump(interface,IF_TRACE_OUT,TRACE_IP,bp);
- (*interface->raw)(interface,bp);
- }
- /* Send a raw slfp frame -- also trivial */
- slfp_raw(interface,bp)
- struct interface *interface;
- struct mbuf *bp;
- {
- /* Make "asy" interface a shadow of the SLFP interface */
- asy_interface.trace = interface->trace ;
-
- /* Queue a frame on the slfp output queue and start transmitter */
- slfpq(interface->dev,bp);
- }
- /* Encode a raw packet in slfp framing, put on link output queue, and kick
- * transmitter
- */
- static
- slfpq(dev,bp)
- int16 dev; /* Serial line number */
- struct mbuf *bp; /* Buffer to be sent */
- {
- register struct slfp *sp;
- struct mbuf *slfp_encode();
-
- if((bp = slfp_encode(dev,bp)) == NULLBUF)
- return;
-
- sp = &slfp[dev];
- enqueue(&sp->sndq,bp);
- dump(&asy_interface,IF_TRACE_OUT,TRACE_SLFP,bp);
- sp->sndcnt++;
- if(sp->tbp == NULLBUF)
- slfp_asy_start(dev);
- }
-
- /* Handle REQ-ACK Timer expiration
- */
- void
- slfp_req_notify(dev)
- int16 dev;
- {
- register struct slfp *sp;
- register struct timer *rt ; /* Timer for REQ-ACK negotiation */
- struct mbuf *bp ;
-
- sp = &slfp[dev];
- rt = &(sp->req_timer) ;
- if (sp->reqcnt++ >= 10) {
- sp->tbp = NULLBUF ;
- sp->req_pending = 0 ;
- bp = dequeue(&sp->sndq);
- sp->sndcnt--;
- free_p(bp) ;
- }
- else {
- set_timer(rt,2000) ; /* 2-Second Timeout for ACK */
- start_timer(rt) ;
- asy_output(dev, slfp_req, REQ_LEN) ;
- }
- }
-
- /* Start output, if possible, on asynch device dev */
- static
- slfp_asy_start(dev)
- int16 dev;
- {
- register struct slfp *sp;
- register struct timer *rt ; /* Timer for REQ-ACK negotiation */
- struct mbuf *bp ;
-
- if(!stxrdy(dev))
- return; /* Transmitter not ready */
-
- sp = &slfp[dev];
- bp = sp->tbp ;
- if(bp != NULLBUF){
- /* transmission just completed */
- free_p(bp) ;
- sp->tbp = NULLBUF;
- }
- if(sp->sndq == NULLBUF)
- return; /* No work */
-
- rt = &(sp->req_timer) ;
- if (sp->req_pending)
- return ;
- sp->reqcnt = 0 ;
- sp->req_pending = 1 ;
- rt->func = slfp_req_notify ;
- rt->arg = (char *)dev ;
- set_timer(rt,2000) ; /* 2-Second Timeout for ACK */
- start_timer(rt) ;
- asy_output(dev, slfp_req, REQ_LEN) ;
- }
- /* Encode a packet in SL/FP format */
- static
- struct mbuf *
- slfp_encode(dev,bp)
- int16 dev; /* Serial line number */
- struct mbuf *bp;
- {
- struct mbuf *lbp; /* Mbuf containing line-ready packet */
- register char *cp;
- char c;
-
- /* Allocate output mbuf that's twice as long as the packet.
- * This is a worst-case guess (consider a packet full of SLFP_ENDs!)
- */
- lbp = alloc_mbuf(HDR_LEN + 2*len_mbuf(bp) + 2);
- if(lbp == NULLBUF){
- /* No space; drop */
- free_p(bp);
- return NULLBUF;
- }
- cp = lbp->data;
-
- /* Prefix packet with the Correct Link-Level Header */
- if (slfp[dev].ar_pending)
- memcpy(cp, ar_hdr, HDR_LEN) ;
- else
- memcpy(cp, ip_hdr, HDR_LEN) ;
- cp += HDR_LEN ;
-
- /* Copy input to output, escaping special characters */
- while(pullup(&bp,&c,1) == 1){
- switch(c & 0xff){
- case SLFP_ESC:
- *cp++ = SLFP_ESC;
- *cp++ = SLFP_ESC - SLFP_ESC;
- break;
- case SLFP_END:
- *cp++ = SLFP_ESC;
- *cp++ = SLFP_END - SLFP_ESC;
- break;
- case SLFP_ACK:
- *cp++ = SLFP_ESC;
- *cp++ = SLFP_ACK - SLFP_ESC;
- break;
- case SLFP_REQ:
- *cp++ = SLFP_ESC;
- *cp++ = SLFP_REQ - SLFP_ESC;
- break;
- default:
- *cp++ = c;
- }
- }
- *cp++ = SLFP_END;
- lbp->cnt = cp - lbp->data;
- return lbp;
- }
-
- #ifdef MSDOS
- /* Invoked when SLFP_REQ is received during xmit of outgoing packet.
- * This allows immediate reception of packet from SCP, rather than
- * forcing it to buffer it until we finish sending the outgoing packet
- */
- static
- unsigned
- slfp_urgent(dev)
- int16 dev; /* SL/FP unit number */
- {
- register struct dma *dp ;
-
- dp = &asy[dev].dma ;
- if (dp->last_octet == SLFP_ESC)
- return 256 ;
- else {
- asy[dev].urgent = NULLCHAR ;
- return SLFP_ACK ;
- }
- }
- #endif
-
- void
- hndl_rcvd_req(dev, sp)
- int16 dev; /* SL/FP unit number */
- register struct slfp *sp;
- {
- char i_state ;
-
- if (sp->reqd) { /* REQ before rcv'g END of last Packet! */
- sp->missed_ends++ ;
- free_p(sp->rbp); /* throw away current packet */
- sp->rbp = NULLBUF;
- sp->rcnt = 0;
- }
-
- sp->reqd = 1 ;
- i_state = disable() ;
- #ifdef MSDOS
- if (asy[dev].dma.flags)
- asy[dev].urgent = slfp_urgent ;
- else
- #endif
- asy_output(dev, slfp_ack, ACK_LEN) ;
- restore(i_state) ;
- }
-
- void
- hndl_rcvd_ack(dev, sp)
- int16 dev; /* SL/FP unit number */
- register struct slfp *sp;
- {
- char i_state ;
-
- i_state = disable() ;
- if (sp->req_pending == 0) {
- sp->false_acks++ ;
- restore(i_state) ;
- return ;
- }
- sp->req_pending = 0 ;
- stop_timer(&(sp->req_timer)) ;
- restore(i_state) ;
- sp->tbp = dequeue(&sp->sndq);
- sp->sndcnt--;
- asy_output(dev,sp->tbp->data,sp->tbp->cnt);
- }
-
- /* Process incoming bytes in SL/FP format
- * When a buffer is complete, return it; otherwise NULLBUF
- */
- static
- struct mbuf *
- slfp_decode(dev,c)
- int16 dev; /* SL/FP unit number */
- char c; /* Incoming character */
- {
- struct mbuf *bp;
- register struct slfp *sp;
- unsigned char uc ;
-
- sp = &slfp[dev];
-
- uc = c & 0xff;
- if (uc == SLFP_REQ) {
- hndl_rcvd_req(dev, sp) ;
- return NULLBUF ;
- }
- if (uc == SLFP_ACK) {
- hndl_rcvd_ack(dev, sp) ;
- return NULLBUF ;
- }
- if (sp->reqd == 0) {
- return NULLBUF ;
- }
-
- switch(uc){
- case SLFP_END:
- sp->reqd = 0 ;
- /* Kick upstairs */
- bp = sp->rbp;
- sp->rbp = NULLBUF;
- sp->rcnt = 0;
- return bp;
- break ;
-
- case SLFP_ESC:
- sp->escaped = 1;
- return NULLBUF;
- }
- if(sp->escaped){
- sp->escaped = 0;
- uc += SLFP_ESC;
- switch(uc){
- case SLFP_ESC:
- case SLFP_REQ:
- case SLFP_ACK:
- case SLFP_END:
- break;
- default:
- uc -= SLFP_ESC;
- sp->bad_esc++;
- sp->errors++;
- }
- }
- /* We reach here with a character for the buffer;
- * make sure there's space for it
- */
- if(sp->rbp == NULLBUF){
- /* Allocate first mbuf for new packet */
- if((sp->rbp1 = sp->rbp = alloc_mbuf(SLFP_ALLOC)) == NULLBUF)
- return NULLBUF; /* No memory, drop */
- sp->rcp = sp->rbp->data;
- } else if(sp->rbp1->cnt == SLFP_ALLOC){
- /* Current mbuf is full; link in another */
- if((sp->rbp1->next = alloc_mbuf(SLFP_ALLOC)) == NULLBUF){
- /* No memory, drop whole thing */
- free_p(sp->rbp);
- sp->rbp = NULLBUF;
- sp->rcnt = 0;
- return NULLBUF;
- }
- sp->rbp1 = sp->rbp1->next;
- sp->rcp = sp->rbp1->data;
- }
- /* Store the character, increment fragment and total
- * byte counts
- */
- *sp->rcp++ = uc;
- sp->rbp1->cnt++;
- sp->rcnt++;
- return NULLBUF;
- }
- /* Process SL/FP line I/O */
- int
- doslfp(interface)
- struct interface *interface;
- {
- char c;
- struct mbuf *bp;
- int16 dev;
- int16 asy_recv();
-
- dev = interface->dev;
- /* Process any pending input */
- while(asy_recv(dev,&c,1) != 0)
- if((bp = slfp_decode(dev,c)) != NULLBUF) {
- (*slfp[dev].recv)(interface,bp);
- }
-
- /* Kick the transmitter if it's idle */
- if(stxrdy(dev))
- slfp_asy_start(dev);
- }
-
- /* Handle Address Reply packets
- */
- void
- addr_reply(dev,bp)
- int16 dev;
- struct mbuf *bp;
- {
- if (len_mbuf(bp) != 4) { /* Invalid Address Response */
- free_p(bp) ;
- return ;
- }
- if (!slfp[dev].ar_pending) {
- free_p(bp) ;
- return ;
- }
- stop_timer(&slfp[dev].ar_timer) ;
- slfp[dev].ar_pending = 0 ;
- ip_addr = (unsigned char)(*bp->data++) ;
- ip_addr <<= 8 ;
- ip_addr |= (unsigned char)(*bp->data++) ;
- ip_addr <<= 8 ;
- ip_addr |= (unsigned char)(*bp->data++) ;
- ip_addr <<= 8 ;
- ip_addr |= (unsigned char)(*bp->data++) ;
- free_p(bp) ;
- }
-
- /* Unwrap incoming SL/FP packets -- use link level header to determine
- * howto handle packet
- */
- slfp_recv(interface,bp)
- struct interface *interface;
- struct mbuf *bp;
- {
- void ip_route();
-
- dump(interface,IF_TRACE_IN,TRACE_SLFP,bp);
- if (len_mbuf(bp) < HDR_LEN) {
- free_p(bp) ;
- return ;
- }
- if (memcmp(ip_hdr, bp->data, HDR_LEN) == 0) {
- bp->data += HDR_LEN ;
- bp->cnt -= HDR_LEN ;
- dump(interface,IF_TRACE_IN,TRACE_IP,bp);
- ip_route(bp,0); /* By def'n, all inbound packets are addr'd to us */
- }
- else if (memcmp(ar_hdr, bp->data, HDR_LEN) == 0) {
- bp->data += HDR_LEN ;
- bp->cnt -= HDR_LEN ;
- dump(interface,IF_TRACE_IN,TRACE_IP,bp);
- addr_reply(interface->dev,bp);
- } else {
- dump(interface,IF_TRACE_IN,TRACE_IP,bp);
- free_p(bp) ;
- }
- }
-
- int
- slfp_dump(bpp, check)
- struct mbuf **bpp;
- int check ;
- {
- if(bpp == NULLBUFP || *bpp == NULLBUF)
- return 0;
-
- printf("SLFP: ");
- /* Sneak peek at IP header and find length */
- if (len_mbuf(*bpp) < HDR_LEN) {
- printf("packet too short!\n");
- return 0;
- }
-
- if (memcmp(ip_hdr, (*bpp)->data, HDR_LEN) == 0)
- printf("IP Packet\n") ;
- else if (memcmp(ar_hdr, (*bpp)->data, HDR_LEN) == 0)
- printf("Addr Req Packet\n") ;
- else
- printf("bad header\n") ;
-
- return 0 ;
- }
-